package com.aegis.redis;

import com.aegis.core.dao.AegisDao;
import com.aegis.core.model.HashScanResult;
import com.aegis.core.model.ScanResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

public class RedisAegisDao implements AegisDao {

    private StringRedisTemplate stringRedisTemplate;
    private RedisTemplate<String, Object> objectRedisTemplate;
    private boolean isInit = false;

    @Autowired
    public void init(RedisConnectionFactory connectionFactory) {
        if (!isInit) {
            // 初始化序列化器
            StringRedisSerializer keySerializer = new StringRedisSerializer();
            GenericJackson2JsonRedisSerializer valueSerializer = new GenericJackson2JsonRedisSerializer();

            // 配置StringRedisTemplate
            stringRedisTemplate = new StringRedisTemplate();
            stringRedisTemplate.setConnectionFactory(connectionFactory);
            stringRedisTemplate.afterPropertiesSet();

            // 配置ObjectRedisTemplate
            objectRedisTemplate = new RedisTemplate<>();
            objectRedisTemplate.setConnectionFactory(connectionFactory);
            objectRedisTemplate.setKeySerializer(keySerializer);
            objectRedisTemplate.setHashKeySerializer(keySerializer);
            objectRedisTemplate.setValueSerializer(valueSerializer);
            objectRedisTemplate.setHashValueSerializer(valueSerializer);
            objectRedisTemplate.afterPropertiesSet();

            isInit = true;
        }
    }

    // ============================= Common =============================
    @Override
    public Object getObject(String key) {
        return objectRedisTemplate.opsForValue().get(key);
    }

    @Override
    public Object getAndDel(String key) {
        return objectRedisTemplate.opsForValue().getAndDelete(key);
    }

    @Override
    public void del(String key) {
        objectRedisTemplate.delete(key);
    }


    @Override
    public boolean setObject(String key, Object value) {
        try {
            objectRedisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public boolean setObject(String key, Object value, long time) {
        try {
            if (time > 0) {
                objectRedisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                setObject(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public boolean setObject(String key, Object value, long time, TimeUnit timeUnit) {
        try {
            if (time > 0) {
                objectRedisTemplate.opsForValue().set(key, value, time, timeUnit);
            } else {
                setObject(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("Increment factor must be greater than 0");
        }
        return objectRedisTemplate.opsForValue().increment(key, delta);
    }

    @Override
    public long decr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("Decrement factor must be greater than 0");
        }
        return objectRedisTemplate.opsForValue().increment(key, -delta);
    }

    // ============================= Hash =============================
    @Override
    public Object hget(String key, String item) {
        return objectRedisTemplate.opsForHash().get(key, item);
    }

    @Override
    public Map<Object, Object> hmget(String key) {
        return objectRedisTemplate.opsForHash().entries(key);
    }


    @Override
    public boolean hmset(String key, Map<String, Object> map) {
        try {
            objectRedisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public boolean hmset(String key, Map<String, Object> map, long time) {
        try {
            objectRedisTemplate.opsForHash().putAll(key, map);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public boolean hset(String key, String item, Object value) {
        try {
            objectRedisTemplate.opsForHash().put(key, item, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public boolean hset(String key, String item, Object value, long time) {
        try {
            objectRedisTemplate.opsForHash().put(key, item, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public void hdel(String key, Object... item) {
        objectRedisTemplate.opsForHash().delete(key, item);
    }

    @Override
    public boolean hHasKey(String key, String item) {
        return objectRedisTemplate.opsForHash().hasKey(key, item);
    }

    @Override
    public double hincr(String key, String item, double by) {
        return objectRedisTemplate.opsForHash().increment(key, item, by);
    }

    @Override
    public double hdecr(String key, String item, double by) {
        return objectRedisTemplate.opsForHash().increment(key, item, -by);
    }

    @Override
    public <T> void scanHash(String key, int count, String pattern, Function<HashScanResult<T>, Boolean> function) {
        ScanOptions options = ScanOptions.scanOptions().match(pattern).count(count).build();
        int currentCount = 0;
        try (Cursor<Map.Entry<Object, Object>> cursorResult = objectRedisTemplate.opsForHash().scan(key, options)) {
            Map<String, T> result = new HashMap<>();
            while (cursorResult.hasNext()) {
                currentCount++;
                Map.Entry<Object, Object> next = cursorResult.next();
                // 将 key 和 value 转换为 String 和 T
                String mapKey = next.getKey().toString();
                T mapValue = (T) next.getValue();  // 假设你的 value 是 T 类型，可能需要做类型转换
                result.put(mapKey, mapValue);
                if (currentCount == count || cursorResult.getCursorId() == 0) {
                    currentCount = 0;
                    if (Boolean.FALSE.equals(function.apply(new HashScanResult<>(result, cursorResult.getCursorId())))) {
                        break;
                    }
                    result.clear();
                }
            }
        } catch (Exception e) {
        }
    }

    // ============================= Set =============================
    @Override
    public Set<Object> sGet(String key) {
        try {
            return objectRedisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public boolean sHasKey(String key, Object value) {
        try {
            return objectRedisTemplate.opsForSet().isMember(key, value);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public long sSet(String key, Object... values) {
        try {
            return objectRedisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    @Override
    public long sSetAndTime(String key, long time, Object... values) {
        try {
            Long count = objectRedisTemplate.opsForSet().add(key, values);
            if (time > 0) {
                expire(key, time);
            }
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    @Override
    public long sGetSetSize(String key) {
        try {
            return objectRedisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    @Override
    public long setRemove(String key, Object... values) {
        try {
            return objectRedisTemplate.opsForSet().remove(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    @Override
    public Object eval(String script, List<String> keys, List<String> args) {
        try {
            return objectRedisTemplate.execute(new DefaultRedisScript<>(script, Object.class), keys, args.toArray());
        } catch (Exception e) {
            throw new RuntimeException("Failed to execute Lua script", e);
        }
    }

    // ... 其他列表方法保持类似实现模式

    // ============================= Keys =============================


    // ============================= Helper =============================
    private boolean expire(String key, long time) {
        try {
            if (time > 0) {
                objectRedisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

}